Skip to content

fix(next-mdx): forward plugin load errors to webpack callback#94119

Open
Jinoko01 wants to merge 4 commits into
vercel:canaryfrom
Jinoko01:fix/next-mdx-unhandled-promise-rejection
Open

fix(next-mdx): forward plugin load errors to webpack callback#94119
Jinoko01 wants to merge 4 commits into
vercel:canaryfrom
Jinoko01:fix/next-mdx-unhandled-promise-rejection

Conversation

@Jinoko01
Copy link
Copy Markdown

@Jinoko01 Jinoko01 commented May 26, 2026

What?

Add .catch() handler to the Promise in mdx-js-loader.js so that
plugin resolution failures are properly forwarded to webpack's error callback.

Why?

When a plugin string path fails to resolve (e.g. typo in plugin name,
missing package), getOptions() returns a rejected Promise. Without
.catch(), webpack's callback is never called, causing the build to
hang indefinitely or throw an unhandled promise rejection instead of
a clear error message.

How?

Chain .catch((error) => callback(error)) after the existing .then()
in nextMdxLoader.

Fixes #94118

Copy link
Copy Markdown
Contributor

@timneutkens timneutkens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test for the changes. You can use pnpm new-test (or ask an AI agent)

@Jinoko01
Copy link
Copy Markdown
Author

Jinoko01 commented May 28, 2026

Can you add a test for the changes. You can use pnpm new-test (or ask an AI agent)

Thanks for the review! I've added a test in test/e2e/app-dir/mdx/mdx.test.ts that verifies the error is properly forwarded to webpack's callback when a plugin path fails to resolve.

The test patches next.config.ts with a non-existent remark plugin, attempts a production build, and asserts two things:

  1. The plugin name appears in the CLI output — confirming the error was passed to webpack's callback
  2. UnhandledPromiseRejection does not appear — confirming the rejection is handled, not silently swallowed

@Jinoko01 Jinoko01 requested a review from timneutkens May 28, 2026 08:28
Comment thread test/e2e/app-dir/mdx/mdx.test.ts Outdated
skipStart: true,
})

if (!isNextStart) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will still create the isolated dir before skipping for dev. It's preferable to skip based on importing isNextStart from e2e-utils in this case.

import { nextTestSetup, isNextStart } from 'e2e-utils'

Then use it to skip the describe:

;(isNextStart ? describe : describe.skip)('mdx without-mdx-rs - invalid plugin error handling', () => {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Updated to import isNextStart directly from e2e-utils and wrap the describe with (isNextStart ? describe : describe.skip)(...). This way nextTestSetup is never called in dev mode, so the isolated directory won't be created unnecessarily.

Replace the early-return guard with (isNextStart ? describe : describe.skip)
so that nextTestSetup — and the isolated directory creation — is never
called outside of next start runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@next/mdx: unhandled promise rejection when plugin path fails to resolve in mdx-js-loader

2 participants